package database;

import dataprocess.HighScoreDb;
import dataprocess.QuestionsDb;

import java.io.File;
import java.io.IOException;
import java.util.Set;

import dataprocess.Parser;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import triviaException.TriviaException;

/**
 * Base de datos de la trivia. Contiene toda la información en memoria
 * del juego.
 * @author Grupo 11 de Programacion Orientada a Objetos
 *
 */

public final class TriviaDbase {
	private Category[] categories;
	private Highscore[] highscores;
	private HighScoreDb highscoredb;
    private Parser parser;
    private QuestionsDb qdb;
    private File qfile, easyFile, mediumFile, hardFile, qdir, hdir;
	
    /**
	 * Crea una instancia de TriviaDbase.
	 * @throws IOException si no puede leer alguno de los
	 * datos en disco
	 */
    
	public TriviaDbase() throws IOException {
		super();
	
		categories = new Category[3];
		highscores = new Highscore[3];
			
		for (int i = 0 ; i < 3 ; i++) {
			categories[i] = new Category(i);
		}
		
		initFiles();
		highscoredb = new HighScoreDb();
        parser = new Parser();
        qdb = new QuestionsDb();
        
	}
	
	/**
	 * Agrega una nueva pregunta a la base de datos, categorizada
	 * según la dificultad de la pregunta.
	 * @param question - nueva pregunta
	 */
	
	public void newQuestion(Question question) {
		categories[question.getLevel()].addQuestion(question);
		return;
	}
	
	/**
	 * Remueve una pregunta de la base de datos de la categoría
	 * que corresponda. Si no existe, no hace nada.
	 */
	
	public void removeQuestion(Question question) {
		categories[question.getLevel()].removeQuestion(question);
        return;
	}
	
	/**
	 * Envía a una instancia de la clase highscore, un Score, en caso
	 * de ser superior a alguno de los top 10, actualiza la base de datos
	 * de puntajes.
	 * @param level - dificultad, decide a que base de datos de puntaje se envía
	 * @param score - puntaje hecho por el usuario
	 * @throws IOException - Si no se pudo actualizar la base de datos de puntajes
	 */
	
	public void trySetNewScore(int level, Score score) throws IOException {
		// Hay que validar el int level
		boolean updHighScoredb;
		updHighScoredb = highscores[level].setHighScore(score);
		
		if (updHighScoredb) {
			highscoredb.saveHighScoresToFile(highscores[level], level);
		}
		return;
	}
	
	/**
	 * Devuelve un arreglo ordenado del top 10 de puntajes
	 * de una cierta categoria.
	 * @param level - categoría elegida
	 * @return Score[] - Arreglo de puntajes
	 */
	
	public Score[] getHighScoresByLevel(int level) {
		return highscores[level].getHighScores();
	}
	
	/**
	 * Descarga a disco todos los puntajes
	 * @throws IOException - si no pudo salvar los puntajes
	 */
	
	public void saveHighScores() throws IOException {
		
		for (int i = 0 ; i < highscores.length ; i++) {
			highscoredb.saveHighScoresToFile(highscores[i], i);
		}
		return;
	}
	
	/**
	 * Carga de archivo a la base de datos, todos los puntajes de cada categoría.
	 * @throws NumberFormatException - Si el archivo está corrupto y contiene datos
	 * inválidos.
	 * @throws TriviaException - Si los puntajes son inválidos
	 */
	
	public void loadHighScores() throws NumberFormatException, TriviaException {
		
		for (int i = 0 ; i < highscores.length ; i++) {
			highscores[i] = highscoredb.loadHighScoresFromFile(i);
		}
			return;
	}
			
	/**
	 * Lee desde archivo, todas las preguntas hacia la base de datos.
	 * @throws TriviaException - Cualquier tipo de error en el archivo
	 * de preguntas
	 */
	
	
	public void loadQuestions() throws TriviaException {
		Set<Question> qset = null;
        Iterator<Question> qiterator;
        Question q;

        qset = parser.readFiles();
        qiterator = qset.iterator();
        
        /** Agrega en la categoria que corresponda las preguntas
        * de la base de datos.
        */
        while(qiterator.hasNext()) {
        	q = (Question) qiterator.next();
        	newQuestion(q);
        }
        
		return;
	}
	
	/**
	 * Salva en archivo todas las preguntas que se encuentran
	 * actualmente en la base de datos.
	 * @throws IOException - imposibilidad de salvar al archivo 
	 */
	
	public void saveQuestions() throws IOException {
		List<Question> qlist = new LinkedList<Question>();
		List<Question> qaux;
		
		// Arma una lista con el listado de TODAS las peguntas
		for (int i = 0 ; i < categories.length ; i++) {
			qaux = categories[i].getAll();
			
			if (qaux != null)
				qlist.addAll(qaux);
		}
		
		qdb.writeFiles(qlist);
		qlist = null;
		return;
	}
	
	/**
	 * Devuelve un listado de todas las preguntas en cierta categoria.
	 * Si la categoria pedida es inválida se devuelve la default (0)
	 * @param level - categoria pedida
	 * @return LinkedList<Question> listado de preguntas de esa categoria
	 */
	
    public LinkedList<Question> getAllQuestions(int level) {
    	if (level < 0 || level > 2)
    		return categories[0].getAll();
    	else
    		return categories[level].getAll();
    }
    
    /**
	 * Devuelve un muestreo de 10 preguntas al azar dentro
	 * de ésta categoria. De no existir esa cantidad, se retorna
	 * null. Las 10 preguntas elegidas son al azar.
	 * @return LinkedList<Question> - 10 preguntas cualesquiera
	 */
    
    public LinkedList<Question> getTriviaQuestions(int level) {
        LinkedList<Question> qlist = null;
        
        qlist = categories[level].getTriviaQuestions();
        return qlist;
    }
    
    /**
	 * Inicia los archivos, creando los que no existan, vacios.
	 * Crea las carpetas y subcarpetas necesarias para mantener
	 * el buen funcionamiento de la aplicación
	 * @throws IOException - Si no puede crear los archivos
	 * que no existan.
	 */
    
    private void initFiles() throws IOException {
    	qdir = new File("data/Questions");
    	hdir = new File("data/HighScores");
        qfile = new File("data/Questions/preguntas.txt");
        easyFile = new File("data/HighScores/easy.hs");
        mediumFile = new File("data/HighScores/medium.hs");
        hardFile = new File("data/HighScores/hard.hs");

        if (!qdir.exists())
        	qdir.mkdirs();
        
        if (!hdir.exists())
        	hdir.mkdir();
        
        if(!qfile.exists() )
    		qfile.createNewFile();
    	
        if(!easyFile.exists() )
    		easyFile.createNewFile();
        
        if (!mediumFile.exists())
        	mediumFile.createNewFile();
        
        if (!hardFile.exists())
        	hardFile.createNewFile();
        
        return;
    }
 }
